#include <gtest/gtest.h>

#include <evolutive/impl/MesoscopicNetworkBuilder.h>

#include <localview/LocalView.h>
#include <localview/Network.h>
#include <localview/Node.h>
#include <localview/Edge.h>
#include <localview/Link.h>
#include <localview/Phase.h>

#include <Network.h>
#include <SimulationNode.h>
#include <NodeSource.h>
#include <NodeInput.h>
#include <TLSPhase.h>
#include <TLSLink.h>

#include <controller/Case.h>

#include <log.h>
#include <Exception.h>

#include <vector>

localview::Network* buildLVNetwork() {
	localview::Network* net = new localview::Network();

	localview::Node* node0_0 = new localview::Node("node0_0");
	localview::Node* node0_1 = new localview::Node("node0_1");
	localview::Node* node0_2 = new localview::Node("node0_2");
	localview::Node* node1_0 = new localview::Node("node1_0");
	localview::Node* node1_1 = new localview::Node("node1_1");
	localview::Node* node1_2 = new localview::Node("node1_2");
	localview::Node* node2_0 = new localview::Node("node2_0");
	localview::Node* node2_1 = new localview::Node("node2_1");
	localview::Node* node2_2 = new localview::Node("node2_2");
	localview::Node* node3_0 = new localview::Node("node3_0");
	localview::Node* node3_1 = new localview::Node("node3_1");
	localview::Node* node3_2 = new localview::Node("node3_2");

	localview::Edge* edg0_0x1_0 = new localview::Edge(node0_0, node1_0, "0_0x1_0");
	net->addEdge(edg0_0x1_0);

	localview::Edge* edg1_0x2_0 = new localview::Edge(node1_0, node2_0, "1_0x2_0");
	edg1_0x2_0->setFlush(10);
	net->addEdge(edg1_0x2_0);

	localview::Edge* edg1_1x1_0 = new localview::Edge(node1_1, node1_0, "1_1x1_0");
	edg1_1x1_0->setFlush(2);
	net->addEdge(edg1_1x1_0);

	localview::Edge* edg1_1x2_1 = new localview::Edge(node1_1, node2_1, "1_1x2_1");
	net->addEdge(edg1_1x2_1);

	localview::Edge* edg1_2x1_1 = new localview::Edge(node1_2, node1_1, "1_2x1_1");
	net->addEdge(edg1_2x1_1);

	localview::Edge* edg0_1x1_1 = new localview::Edge(node0_1, node1_1, "0_1x1_1");
	net->addEdge(edg0_1x1_1);

	localview::Edge* edg2_1x3_1 = new localview::Edge(node2_1, node3_1, "2_1x3_1");
	net->addEdge(edg2_1x3_1);

	localview::Edge* edg2_1x2_2 = new localview::Edge(node2_1, node2_2, "2_1x2_2");
	net->addEdge(edg2_1x2_2);

	localview::Edge* edg2_0x2_1 = new localview::Edge(node2_0, node2_1, "2_0x2_1");
	net->addEdge(edg2_0x2_1);

	localview::Edge* edg2_0x3_0 = new localview::Edge(node2_0, node3_0, "2_0x3_0");
	net->addEdge(edg2_0x3_0);

	localview::Edge* edg2_2x1_2 = new localview::Edge(node2_2, node1_2, "2_2x1_2");
	net->addEdge(edg2_2x1_2);

	localview::Edge* edg3_2x2_2 = new localview::Edge(node3_2, node2_2, "3_2x2_2");
	net->addEdge(edg3_2x2_2);

	localview::Edge* edg1_2x0_2 = new localview::Edge(node1_2, node0_2, "1_2x0_2");
	net->addEdge(edg1_2x0_2);

	/* Phases */
	//node1_1
	localview::Link*  link_1_1_l0 = new localview::Link(node1_2, node1_0);
	localview::Link*  link_1_1_l1 = new localview::Link(node1_2, node2_1);
	localview::Link*  link_1_1_l2 = new localview::Link(node0_1, node1_0);
	localview::Link*  link_1_1_l3 = new localview::Link(node0_1, node2_1);

	localview::Phase* phase_1_1_p0 = new localview::Phase();
	phase_1_1_p0->setDuration(30);
	phase_1_1_p0->addLink(link_1_1_l0, 'G');
	phase_1_1_p0->addLink(link_1_1_l1, 'G');
	phase_1_1_p0->addLink(link_1_1_l2, 'r');
	phase_1_1_p0->addLink(link_1_1_l3, 'r');
	phase_1_1_p0->setNext(node1_0);

	localview::Phase* phase_1_1_p1 = new localview::Phase();
	phase_1_1_p1->setDuration(5);
	phase_1_1_p1->addLink(link_1_1_l0, 'y');
	phase_1_1_p1->addLink(link_1_1_l1, 'y');
	phase_1_1_p1->addLink(link_1_1_l2, 'r');
	phase_1_1_p1->addLink(link_1_1_l3, 'r');
	phase_1_1_p1->setNext(node1_0);

	localview::Phase* phase_1_1_p2 = new localview::Phase();
	phase_1_1_p2->setDuration(30);
	phase_1_1_p2->addLink(link_1_1_l0, 'r');
	phase_1_1_p2->addLink(link_1_1_l1, 'r');
	phase_1_1_p2->addLink(link_1_1_l2, 'G');
	phase_1_1_p2->addLink(link_1_1_l3, 'G');
	phase_1_1_p2->setNext(node1_0);

	localview::Phase* phase_1_1_p3 = new localview::Phase();
	phase_1_1_p3->setDuration(5);
	phase_1_1_p3->addLink(link_1_1_l0, 'r');
	phase_1_1_p3->addLink(link_1_1_l1, 'r');
	phase_1_1_p3->addLink(link_1_1_l2, 'y');
	phase_1_1_p3->addLink(link_1_1_l3, 'y');
	phase_1_1_p3->setNext(node1_0);

	node1_1->addLink(link_1_1_l0);
	node1_1->addLink(link_1_1_l1);
	node1_1->addLink(link_1_1_l2);
	node1_1->addLink(link_1_1_l3);
	node1_1->addPhase(phase_1_1_p0);
	node1_1->addPhase(phase_1_1_p1);
	node1_1->addPhase(phase_1_1_p2);
	node1_1->addPhase(phase_1_1_p3);

	//node2_1
	localview::Link*  link_2_1_l0 = new localview::Link(node2_0, node3_1);
	localview::Link*  link_2_1_l1 = new localview::Link(node2_0, node2_2);
	localview::Link*  link_2_1_l2 = new localview::Link(node1_1, node3_1);
	localview::Link*  link_2_1_l3 = new localview::Link(node1_1, node2_2);

	localview::Phase* phase_2_1_p0 = new localview::Phase();
	phase_2_1_p0->setDuration(30);
	phase_2_1_p0->addLink(link_2_1_l0, 'G');
	phase_2_1_p0->addLink(link_2_1_l1, 'G');
	phase_2_1_p0->addLink(link_2_1_l2, 'r');
	phase_2_1_p0->addLink(link_2_1_l3, 'r');
	phase_2_1_p0->setNext(node2_0);

	localview::Phase* phase_2_1_p1 = new localview::Phase();
	phase_2_1_p1->setDuration(5);
	phase_2_1_p1->addLink(link_2_1_l0, 'y');
	phase_2_1_p1->addLink(link_2_1_l1, 'y');
	phase_2_1_p1->addLink(link_2_1_l2, 'r');
	phase_2_1_p1->addLink(link_2_1_l3, 'r');
    phase_2_1_p1->setNext(node2_0);

	localview::Phase* phase_2_1_p2 = new localview::Phase();
	phase_2_1_p2->setDuration(30);
	phase_2_1_p2->addLink(link_2_1_l0, 'r');
	phase_2_1_p2->addLink(link_2_1_l1, 'r');
	phase_2_1_p2->addLink(link_2_1_l2, 'G');
	phase_2_1_p2->addLink(link_2_1_l3, 'G');
    phase_2_1_p2->setNext(node2_0);

	localview::Phase* phase_2_1_p3 = new localview::Phase();
	phase_2_1_p3->setDuration(5);
	phase_2_1_p3->addLink(link_2_1_l0, 'r');
	phase_2_1_p3->addLink(link_2_1_l1, 'r');
	phase_2_1_p3->addLink(link_2_1_l2, 'y');
	phase_2_1_p3->addLink(link_2_1_l3, 'y');
    phase_2_1_p3->setNext(node2_0);

	node2_1->addLink(link_2_1_l0);
	node2_1->addLink(link_2_1_l1);
	node2_1->addLink(link_2_1_l2);
	node2_1->addLink(link_2_1_l3);
	node2_1->addPhase(phase_2_1_p0);
	node2_1->addPhase(phase_2_1_p1);
	node2_1->addPhase(phase_2_1_p2);
	node2_1->addPhase(phase_2_1_p3);

	//node1_0
	localview::Link*  link_1_0_l0 = new localview::Link(node1_1, node2_0);
	localview::Link*  link_1_0_l1 = new localview::Link(node0_0, node2_0);

	localview::Phase* phase_1_0_p0 = new localview::Phase();
	phase_1_0_p0->setDuration(30);
	phase_1_0_p0->addLink(link_1_0_l0, 'G');
	phase_1_0_p0->addLink(link_1_0_l1, 'r');
	phase_1_0_p0->setNext(node2_0);

	localview::Phase* phase_1_0_p1 = new localview::Phase();
	phase_1_0_p1->setDuration(5);
	phase_1_0_p1->addLink(link_1_0_l0, 'y');
	phase_1_0_p1->addLink(link_1_0_l1, 'r');
	phase_1_0_p1->setNext(node2_0);

	localview::Phase* phase_1_0_p2 = new localview::Phase();
	phase_1_0_p2->setDuration(30);
	phase_1_0_p2->addLink(link_1_0_l0, 'r');
	phase_1_0_p2->addLink(link_1_0_l1, 'G');
	phase_1_0_p2->setNext(node2_0);

	localview::Phase* phase_1_0_p3 = new localview::Phase();
	phase_1_0_p3->setDuration(5);
	phase_1_0_p3->addLink(link_1_0_l0, 'r');
	phase_1_0_p3->addLink(link_1_0_l1, 'y');
	phase_1_0_p3->setNext(node2_0);

	node1_0->addLink(link_1_0_l0);
	node1_0->addLink(link_1_0_l1);
	node1_0->addPhase(phase_1_0_p0);
	node1_0->addPhase(phase_1_0_p1);
	node1_0->addPhase(phase_1_0_p2);
	node1_0->addPhase(phase_1_0_p3);

	//node2_0
	localview::Link*  link_2_0_l0 = new localview::Link(node1_0, node3_0);
	localview::Link*  link_2_0_l1 = new localview::Link(node1_0, node2_1);

	localview::Phase* phase_2_0_p0 = new localview::Phase();
	phase_2_0_p0->setDuration(30);
	phase_2_0_p0->addLink(link_2_0_l0, 'G');
	phase_2_0_p0->addLink(link_2_0_l1, 'G');
	phase_2_0_p0->setNext(node3_0);

	node2_0->addLink(link_2_0_l0);
	node2_0->addLink(link_2_0_l1);
	node2_0->addPhase(phase_2_0_p0);

	//node1_2
	localview::Link*  link_1_2_l0 = new localview::Link(node2_2, node1_1);
	localview::Link*  link_1_2_l1 = new localview::Link(node2_2, node0_2);

	localview::Phase* phase_1_2_p0 = new localview::Phase();
	phase_1_2_p0->setDuration(30);
	phase_1_2_p0->addLink(link_1_2_l0, 'G');
	phase_1_2_p0->addLink(link_1_2_l1, 'G');
	phase_1_2_p0->setNext(node1_1);

	node1_2->addLink(link_1_2_l0);
	node1_2->addLink(link_1_2_l1);
	node1_2->addPhase(phase_1_2_p0);

	//node2_2
	localview::Link*  link_2_2_l0 = new localview::Link(node3_2, node1_2);
	localview::Link*  link_2_2_l1 = new localview::Link(node2_1, node1_2);

	localview::Phase* phase_2_2_p0 = new localview::Phase();
	phase_2_2_p0->setDuration(30);
	phase_2_2_p0->addLink(link_2_2_l0, 'G');
	phase_2_2_p0->addLink(link_2_2_l1, 'r');
	phase_2_2_p0->setNext(node1_2);

	localview::Phase* phase_2_2_p1 = new localview::Phase();
	phase_2_2_p1->setDuration(5);
	phase_2_2_p1->addLink(link_2_2_l0, 'y');
	phase_2_2_p1->addLink(link_2_2_l1, 'r');
	phase_2_2_p1->setNext(node1_2);

	localview::Phase* phase_2_2_p2 = new localview::Phase();
	phase_2_2_p2->setDuration(30);
	phase_2_2_p2->addLink(link_2_2_l0, 'r');
	phase_2_2_p2->addLink(link_2_2_l1, 'G');
	phase_2_2_p2->setNext(node1_2);

	localview::Phase* phase_2_2_p3 = new localview::Phase();
	phase_2_2_p3->setDuration(5);
	phase_2_2_p3->addLink(link_2_2_l0, 'r');
	phase_2_2_p3->addLink(link_2_2_l1, 'y');
	phase_2_2_p3->setNext(node1_2);

	node2_2->addLink(link_2_2_l0);
	node2_2->addLink(link_2_2_l1);
	node2_2->addPhase(phase_2_2_p0);
	node2_2->addPhase(phase_2_2_p1);
	node2_2->addPhase(phase_2_2_p2);
	node2_2->addPhase(phase_2_2_p3);

	return net;
}

TEST(MesoscopicNetworkBuilder, lvNetwork2Mesoscopic) {

	localview::Network* net = buildLVNetwork();
	localview::LocalView localview("node1_1", net);

	MesoscopicNetworkBuilder* builder = new MesoscopicNetworkBuilder();

	Network* mesoNet = 0;

	Case c;

	try {
		mesoNet = builder->lvNetwork2Mesoscopic(&localview, &c, 0);
	}catch(rsi::Exception* c) {
		FAIL() << "Exception: " << c->what();
	}

	ASSERT_TRUE(mesoNet != 0);

	std::vector<std::string> expectedNodes;

	expectedNodes.push_back("node0_0");
	expectedNodes.push_back("node1_0");
	expectedNodes.push_back("node2_0");
	expectedNodes.push_back("node1_1");
	expectedNodes.push_back("node2_1");
	expectedNodes.push_back("node1_2");
	expectedNodes.push_back("node0_1");
	expectedNodes.push_back("node3_1");
	expectedNodes.push_back("node2_2");
	expectedNodes.push_back("node3_0");
	expectedNodes.push_back("node3_2");
	expectedNodes.push_back("node0_2");

	SimulationNode* curSimNode;
	std::vector<SimulationNode*> simNodes;
	std::vector<SimulationNode*>::iterator it;

	simNodes = mesoNet->getNodes();

	int i = 0;
	for(it = simNodes.begin(); it != simNodes.end(); it++) {
		curSimNode = *it;
		std::string strExpected = expectedNodes[i];

		EXPECT_STREQ(strExpected.c_str(), curSimNode->getId().c_str());

		i++;
	}

	/* Test junction 1_1 */
	SimulationNode* snode1_1 = mesoNet->getNode("node1_1");
	localview::Node* node1_1 = net->getNodeByName("node1_1");

	std::vector<TLSPhase*> phases = snode1_1->getPhases();
	std::vector<TLSPhase*>::iterator phasesIt;
	std::vector<TLSLink*> links;
	std::vector<TLSLink*>::iterator linksIt;
	TLSPhase* curPhase;

	EXPECT_EQ(2, (int)phases.size());

	for(phasesIt = phases.begin(); phasesIt != phases.end(); phasesIt++) {
		curPhase = (*phasesIt);

		links = curPhase->getLinks();

		EXPECT_EQ(2, (int)links.size());
	}

	//Test outcoming
	SimulationNode* snode0_0 = mesoNet->getNode("node0_0");

	EXPECT_EQ(1, (int)snode0_0->getOutcomingNodes().size());

	//Test border nodes for NodeSource
	std::vector<SimulationNode*> nodes = mesoNet->getNodes();
    std::vector<NodeSource*> nodeSources = mesoNet->getSources();

	for(unsigned int i = 0; i < nodes.size(); i++) {
	    SimulationNode* n = nodes[i];

	    if(n->isBorder() && (n->getIncomingNodesCount()) == 0 && (n->getOutcomingNodesCount() > 0)) {
	        int sourceFound = 0;

	        for(unsigned int si = 0; si < nodeSources.size(); si++) {
	            NodeSource* ns = nodeSources[si];

	            if(ns->getNode() == n) {
	                sourceFound = 1;
	                break;
	            }
	        }

	        EXPECT_EQ(1, sourceFound);
	    }
	}

	//Test initial cars for edg1_0x2_0
	localview::Edge* edg1_0x2_0 = net->getEdgeByName("1_0x2_0");
	SimulationNode* edg1_0x2_0From = mesoNet->getNode(edg1_0x2_0->getSource()->getId());
	SimulationNode* edg1_0x2_0To   = mesoNet->getNode(edg1_0x2_0->getTarget()->getId());
	NodeInput* edge1_0x2_0Ni = edg1_0x2_0To->getNodeInput(edg1_0x2_0From);

	EXPECT_EQ(10, edge1_0x2_0Ni->getCarsToTransfer());

	//Test initial cars for edge1_1x1_0
	localview::Edge* edg1_1x1_0 = net->getEdgeByName("1_1x1_0");
	SimulationNode* edg1_1x1_0From = mesoNet->getNode(edg1_1x1_0->getSource()->getId());
    SimulationNode* edg1_1x1_0To   = mesoNet->getNode(edg1_1x1_0->getTarget()->getId());
    NodeInput* edg1_1x1_0Ni = edg1_1x1_0To->getNodeInput(edg1_1x1_0From);

    EXPECT_EQ(2, edg1_1x1_0Ni->getCarsToTransfer());

	delete builder;
	delete mesoNet;
}
